package com.loser.gateway.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public final class ChannelUtils {

    private static final AtomicInteger seq = new AtomicInteger();

    private static final ChannelGroup CHANNEL_GROUP = new DefaultChannelGroup("ChannelGroups", GlobalEventExecutor.INSTANCE);

    private static final Map<String, Channel> USER_CHANGE_MAP = new ConcurrentHashMap<>(512);

    private static final AttributeKey<String> USERID_KEY = AttributeKey.valueOf("userId");

    private static final AttributeKey<String> REAL_IP = AttributeKey.valueOf("realId");

    private static final AttributeKey<String> SESSION_KEY = AttributeKey.valueOf("sessionId");

    private static final AttributeKey<Date> CONNECT_TIME_KEY = AttributeKey.valueOf("connectTime");

    public static boolean addChannelUserId(Channel channel, String userId) {
        USER_CHANGE_MAP.put(userId, channel);
        Attribute<String> sessionAttr = channel.attr(USERID_KEY);
        return sessionAttr.compareAndSet(null, userId);
    }

    public static Map<String, Channel> getChangesByUserIds(Iterable<String> userIds) {

        if (CollectionUtil.isEmpty(userIds)) {
            return Maps.newHashMap();
        }
        Map<String, Channel> result = Maps.newHashMap();
        for (String userId : userIds) {
            Channel channel = USER_CHANGE_MAP.get(userId);
            if (Objects.nonNull(channel)) {
                result.put(userId, channel);
            }
        }
        return result;

    }

    public static Channel getChannelByUserId(String userId) {
        return USER_CHANGE_MAP.get(userId);
    }

    public static boolean addRealIp(Channel channel, String realIp) {
        Attribute<String> sessionAttr = channel.attr(REAL_IP);
        return sessionAttr.compareAndSet(null, realIp);
    }


    public static Date getConnectTime(Channel channel) {
        Attribute<Date> sessionAttr = channel.attr(CONNECT_TIME_KEY);
        return sessionAttr.get();
    }

    private static void addConnectTime(Channel channel, Date date) {
        Attribute<Date> sessionAttr = channel.attr(CONNECT_TIME_KEY);
        sessionAttr.compareAndSet(null, date);
    }

    private static String createSessionId(String localIp) {
        return String.format("%s-%s-%s-%s", System.currentTimeMillis(), getRandom(), localIp, seq.incrementAndGet());
    }

    public static boolean addSessionId(Channel channel) {
        Attribute<String> sessionAttr = channel.attr(SESSION_KEY);
        addConnectTime(channel, new Date());
        return sessionAttr.compareAndSet(null, MD5Utils.encodeHexString(createSessionId(channel.localAddress().toString()).getBytes()));
    }


    private static String getRandom() {
        return RandomUtil.randomInt(100000, 999999) + "";
    }

    public static String getSessionId(Channel channel) {
        Attribute<String> sessionAttr = channel.attr(SESSION_KEY);
        return sessionAttr.get();
    }

    public static String getUserIdBy(Channel channel) {
        Attribute<String> sessionAttr = channel.attr(USERID_KEY);
        return sessionAttr.get();
    }

    public static String getIp(Channel channel) {
        Attribute<String> sessionAttr = channel.attr(REAL_IP);
        return sessionAttr.get();
    }


    public static ChannelGroup getChannelGroup() {
        return CHANNEL_GROUP;
    }

    public static void addChannel(Channel channel) {
        CHANNEL_GROUP.add(channel);
        String userId = getUserIdBy(channel);
        if (Objects.nonNull(userId)) {
            USER_CHANGE_MAP.put(userId, channel);
        }
    }

    public static void removeChannel(Channel channel) {
        CHANNEL_GROUP.remove(channel);
        String userId = getUserIdBy(channel);
        if (Objects.nonNull(userId)) {
            USER_CHANGE_MAP.remove(userId);
        }

    }

    public static int getGroupSize() {
        return CHANNEL_GROUP.size();
    }
}
